import numpy as np
import matplotlib.pyplot as plt
from objectTable import ObjectTable
from knapsack import Knapsack
from time import sleep
from tqdm import tqdm
class GA(Knapsack):
    def __init__(self, table, W, n_iter, n_pop, r_cross, r_mut):
        super(GA, self).__init__(table, W)
        #parameter of GA
        self.n_bits = self.n
        self.n_iter = n_iter
        self.n_pop = n_pop
        self.r_cross = r_cross
        self.r_mut = r_mut
        #population
        self.pop = [self.Xs() for i in range(self.n_pop)]
        self.scores = [self.evalVals(X) for X in self.pop]
        '''
        #function of knapsack
        #objFunction
        #solFUnction
        #parameter of knapsack
        '''
        #process
        self.learnRate = list()
        #solution
        self.bestXs = self.X
        self.bestObj = self.objFunc(self.bestXs)
        self.bestIndexObj = list()
        self.minCost = -1
    def objFunc(self, Xs):
        if self.checkXs(Xs):
            return self.evalVals(Xs)
        else:# cannot put those objects into knapsack
            return -1
    def getIndexObj(self,best):
        index = 0
        indexObj = list()
        for i in best:
            if i == 1:
                indexObj.append(index)
            index += 1
        return indexObj
    def selection(self, k = 3):
        selection_ix = np.random.randint(self.n_pop)
        for ix  in np.random.randint(0, len(self.pop) -1 , k - 1):
            if self.scores[ix] < self.scores[selection_ix]:
                selection_ix = ix
        return self.pop[selection_ix]
    def crossover(self, p1, p2):
        c1, c2 = p1.copy(), p2.copy()
        if np.random.rand() < self.r_cross:
            pt = np.random.randint(1, min(len(p1), len(p2)) - 1 - 1) 
            #perform crossover
            c1 = p1[ : pt] + p2[pt : ]
            c2 = p2[ : pt] + p1[pt : ]
        return [c1,c2]
    def mutation(self, p):
        for i in range(len(p)):
            if np.random.rand() < self.r_mut:
                if p[i] == 1:
                    p[i] = 0
                elif p[i] == 0:
                    p[i] = 1
        return p
    def gentic_algorithm(self):
        for gen in tqdm(range(self.n_iter)):
            #mark learning rate
            self.learnRate.append(self.bestObj)
            #evaluate scores of all candiates in the population
            self.scores = [self.objFunc(X) for X in self.pop]
            #check for new best solution
            for i in range(len(self.pop)):
                if self.scores[i] > self.bestObj:
                    self.bestXs, self.bestObj = self.pop[i], self.scores[i]
                    #print(">",gen,"new best f", self.getIndexObj(self.pop[i]),"=", self.scores[i])
            #select parents
            selected = [self.selection() for i in range(len(self.pop))]
            #create the next generation
            children = list()
            for i in range(0, len(self.pop) - 1, 2):
                #get selected parents in pairs
                p1, p2 = selected[i], selected[i+1]
                #crossover the mutation
                for c in self.crossover(p1, p2):
                    #mutation
                    self.mutation(c)
                    #store for next generation
                    children.append(c)
            #replace population
            self.pop = children
        self.bestIndexObj = self.getIndexObj(self.bestXs)
        self.minCost = self.evalCosts(self.bestXs)
    def showLearnRate(self):
        plt.plot(self.learnRate)
        plt.show()


table = np.loadtxt("table.csv",int)
W = int(input("enter W: "))
S = GA(table,W, 10000, 100, 0.4, 0.3)
'''
print("pop: ", S.pop)
print("scores: ",S.scores)
print("Obj: ",S.objFunc([0,0,0,0,1]))
print("crossover: ",S.crossover([1,2,3,4],[2,3,4,5]))
print("mutation: ", S.mutation([1,0,1,0,1]))
print("getIndexObj: ",S.getIndexObj([0,1,0,1]))
'''
print(S.table)
S.gentic_algorithm()
print("mincost:",S.minCost)
print("best: ", S.bestXs)
print("total Value of all object: ", S.totalVals())
print("total cost of all object: ", S.totalCosts())
S.showLearnRate()

